﻿//-----------------------------------------------------------------------
// <copyright file="Tagger.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation.
// All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// This code released under the terms of the 
// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
// </copyright>
// <author>Yogesh M Joshi</author>
// <email>ymjoshi@live.com</email>
// <date>16-May-2011</date>
//-----------------------------------------------------------------------

namespace SmartTag.Common.Taxonomy
{
    using System;
    using System.Collections.Generic;
    using System.Text.RegularExpressions;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Taxonomy;
    using SmartTag.Common.Utility;

    /// <summary>
    /// This class contains item tagging related functions
    /// </summary>
    public static class Tagger
    {
        #region "Private Fields"

        /// <summary>
        /// Object to synchronize operation between different threads
        /// </summary>
        private static object lockObject;

        #endregion

        /// <summary>
        /// Initializes static members of the Tagger class
        /// </summary>
        static Tagger()
        {
            lockObject = new object();
        }

        #region "Properties"

        /// <summary>
        /// Gets the scynchronization object
        /// </summary>
        public static object LockObject
        {
            get
            {
                return lockObject;
            }
        }

        #endregion

        /// <summary>
        /// This function tags the specific item with given terms
        /// The new terms will be added and existing terms will be retained
        /// </summary>
        /// <param name="item">Item to tag</param>
        /// <param name="terms">Collection of terms</param>
        /// <param name="field">Taxonomy field</param>
        /// <param name="stringToSearch">String to extract terms from</param>
        /// <param name="relevence">Relevence of the term</param>
        public static void Tag(SPListItem item, List<Term> terms, TaxonomyField field, string stringToSearch, int relevence)
        {
            try
            {
                Guid fieldId = field.Id;
                TaxonomyFieldValueCollection newValues = new TaxonomyFieldValueCollection(field);

                // We need to get current values first
                TaxonomyFieldValueCollection currentValues = item[fieldId] as TaxonomyFieldValueCollection;

                // Need to retain existing values
                if (currentValues != null && currentValues.Count > 0)
                {
                    foreach (TaxonomyFieldValue value in currentValues)
                    {
                        newValues.Add(value);
                    }
                }

                // Search the string for valid terms
                foreach (Term term in terms)
                {
                    string strEscaped = string.Format(@"\b{0}\b", Regex.Escape(term.Name));
                    Regex regexTerm = new Regex(strEscaped, RegexOptions.IgnoreCase);
                    MatchCollection matches = regexTerm.Matches(stringToSearch);

                    // Check if we got atleast one match
                    // If the values are duplicate SP automatically takes care of it
                    if (matches.Count >= relevence)
                    {
                        TaxonomyFieldValue value = new TaxonomyFieldValue(field);
                        value.Label = term.GetDefaultLabel(1033);
                        value.TermGuid = term.Id.ToString();

                        // Check if item is already tagged with given value
                        if (CheckTaxonomyValueExisits(currentValues, value) == false)
                        {
                            newValues.Add(value);
                        }
                    }
                }

                lock (LockObject)
                {
                    // We got the values now tag the item
                    field.SetFieldValue(item, newValues);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Error occured in Tag", (int)TraceLogEventId.TaggingMethod);
            }
        }

        /// <summary>
        /// Check if the value exists 
        /// </summary>
        /// <param name="currentValues">Current tags</param>
        /// <param name="valueToCheck">Value to be checked inside collection</param>
        /// <returns>True if value exists</returns>
        private static bool CheckTaxonomyValueExisits(TaxonomyFieldValueCollection currentValues, TaxonomyFieldValue valueToCheck)
        {
            return currentValues.Exists(new Predicate<TaxonomyFieldValue>(delegate(TaxonomyFieldValue value)
            {
                if (value.TermGuid.Equals(valueToCheck.TermGuid, StringComparison.OrdinalIgnoreCase))
                {
                    return true;
                }

                return false;
            }));
        }
    }
}
